/*
  Copyright 2007 Anders Båtstrand

      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.

 */
package org.dyndns.anderlin.jmplayer.client.ui;

import org.dyndns.anderlin.jmplayer.model.Piece;
import org.dyndns.anderlin.jmplayer.service.ArtistService;
import org.dyndns.anderlin.jmplayer.service.PieceService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.binding.form.HierarchicalFormModel;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.richclient.application.PageComponentContext;
import org.springframework.richclient.application.event.LifecycleApplicationEvent;
import org.springframework.richclient.application.support.AbstractView;
import org.springframework.richclient.command.CommandGroup;
import org.springframework.richclient.command.support.AbstractActionCommandExecutor;
import org.springframework.richclient.command.support.GlobalCommandIds;
import org.springframework.richclient.dialog.CompositeDialogPage;
import org.springframework.richclient.dialog.ConfirmationDialog;
import org.springframework.richclient.dialog.TabbedDialogPage;
import org.springframework.richclient.dialog.TitledPageApplicationDialog;
import org.springframework.richclient.form.FormModelHelper;
import org.springframework.richclient.progress.TreeStatusBarUpdater;
import org.springframework.richclient.tree.FocusableTreeCellRenderer;
import org.springframework.richclient.util.PopupMenuMouseListener;
import org.springframework.util.Assert;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

public class PieceManagerView extends AbstractView implements InitializingBean, ApplicationListener {

    private JTree pieceTree;

    private DefaultTreeModel pieceTreeModel;

    private PieceService pieceService;

    private ArtistService artistService;

    private DeleteExecutor deleteExecutor = new DeleteExecutor();

    private PropertiesExecutor propertiesExecutor = new PropertiesExecutor();

    private PiecePropertiesExecutor piecePropertiesExecutor = new PiecePropertiesExecutor();

    public void setPieceService(PieceService pieceService) {
        this.pieceService = pieceService;
    }

    public void setArtistService(ArtistService artistService) {
        this.artistService = artistService;
    }

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(pieceService, "A pieceService must be provided.");
        Assert.notNull(artistService, "An artistService must be provided.");
    }

    protected JComponent createControl() {
        JPanel view = new JPanel(new BorderLayout());
        createPieceManagerTree();
        JScrollPane sp = new JScrollPane(pieceTree);
        view.add(sp, BorderLayout.CENTER);
        return view;
    }

    private void addChildren(DefaultMutableTreeNode rootNode, List<Piece> pieces) {
        for (Piece piece : pieces) {
            DefaultMutableTreeNode node = new DefaultMutableTreeNode(piece);
            rootNode.add(node);
            if (piece.getChildren() != null && piece.getChildren().size() > 0) {
                addChildren(node, piece.getChildren());
            }
        }
    }

    private void createPieceManagerTree() {
        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Pieces");
        List<Piece> pieces = pieceService.getRootPieces();
        addChildren(rootNode, pieces);
        this.pieceTreeModel = new DefaultTreeModel(rootNode);
        this.pieceTree = new JTree(pieceTreeModel);
        pieceTree.setShowsRootHandles(true);
        pieceTree.addTreeSelectionListener(new TreeStatusBarUpdater(getStatusBar()) {
            public String getSelectedObjectName() {
                Piece selectedPiece = getSelectedPiece();
                if (selectedPiece != null) {
                    return selectedPiece.getTitle();
                }
                return "Pieces";
            }
        });
        pieceTree.addTreeSelectionListener(new TreeSelectionListener() {
            public void valueChanged(TreeSelectionEvent e) {
                updateCommands();

                // hour-glass while fetching
                pieceTree.setCursor(new Cursor(Cursor.WAIT_CURSOR));
                /**
                 // fetch children of the selected object
                 if (getSelectedPiece() != null) {
                 Piece p = getSelectedPiece();
                 DefaultMutableTreeNode n = getSelectedPieceNode();
                 for (Image i : p.getImages()) {
                 n.add(new DefaultMutableTreeNode(i));
                 }
                 } else if (getSelectedImage() != null) {
                 Image p = getSelectedImage();
                 DefaultMutableTreeNode n = getSelectedImageNode();
                 for (ImageVersion i : p.getVersions()) {
                 n.add(new DefaultMutableTreeNode(i));
                 }
                 }    **/
                // finnished
                pieceTree.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
        });
        pieceTree.addMouseListener(new PopupMenuMouseListener() {
            protected boolean onAboutToShow(MouseEvent e) {
                return !isRootOrNothingSelected();
            }

            protected JPopupMenu getPopupMenu() {
                return getSelectedPiece() != null ? createPiecePopupContextMenu() : null;
            }
        });
        pieceTree.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1 && propertiesExecutor.isEnabled()) {
                    propertiesExecutor.execute();
                }
            }
        });
        pieceTree.setCellRenderer(getTreeCellRenderer());
        pieceTree.setRootVisible(true);
    }

    private void updateCommands() {
        int treeSelectionCount = pieceTree.getSelectionCount();
        if (isRootOrNothingSelected()) {
            deleteExecutor.setEnabled(false);
            propertiesExecutor.setEnabled(false);
        } else if (treeSelectionCount == 1) {
            deleteExecutor.setEnabled(true);
            propertiesExecutor.setEnabled(true);
        } else if (treeSelectionCount > 1) {
            deleteExecutor.setEnabled(true);
            propertiesExecutor.setEnabled(false);
        }
    }

    private boolean isRootOrNothingSelected() {
        return pieceTree.getSelectionCount() == 0
                || (pieceTree.getSelectionCount() == 1 && pieceTree.isRowSelected(0));
    }

    private Piece getSelectedPiece() {
        DefaultMutableTreeNode node = getSelectedPieceNode();
        if (node != null)
            return (Piece) node.getUserObject();

        return null;
    }

    private DefaultMutableTreeNode getSelectedPieceNode() {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) pieceTree.getLastSelectedPathComponent();
        if (node == null || !(node.getUserObject() instanceof Piece))
            return null;

        return node;
    }

    private DefaultTreeCellRenderer treeCellRenderer = new FocusableTreeCellRenderer() {
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
                                                      boolean leaf, int row, boolean hasFocus) {
            super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            if (node.isRoot()) {
                this.setIcon(getIconSource().getIcon("folder.icon"));
            } else if (node.getUserObject() instanceof Piece) {
                Piece piece = (Piece) node.getUserObject();
                this.setText(piece.getTitle());
                this.setIcon(getIconSource().getIcon("piece.bullet"));
            }
            return this;
        }
    };

    public TreeCellRenderer getTreeCellRenderer() {
        return treeCellRenderer;
    }

    protected void registerLocalCommandExecutors(PageComponentContext context) {
        context.register(GlobalCommandIds.DELETE, deleteExecutor);
        context.register(GlobalCommandIds.PROPERTIES, propertiesExecutor);
    }

    /**
     * Handle an application event.
     *
     * @param e the event to respond to
     */

    public void onApplicationEvent(ApplicationEvent e) {
        if (e instanceof LifecycleApplicationEvent) {
            LifecycleApplicationEvent le = (LifecycleApplicationEvent) e;
            if (le.getEventType().equals(LifecycleApplicationEvent.CREATED) && le.objectIs(Piece.class)) {
                if (pieceTree != null) {
                    DefaultMutableTreeNode root = (DefaultMutableTreeNode) pieceTreeModel.getRoot();
                    root.add(new DefaultMutableTreeNode(le.getObject()));
                    pieceTreeModel.nodeStructureChanged(root);
                }
            }
        }
    }

    private JPopupMenu createPiecePopupContextMenu() {
        // delete, addImage separator, properties
        CommandGroup group = getWindowCommandManager().createCommandGroup(
                "pieceViewTablePieceCommandGroup",
                new Object[]{"deleteCommand", "separator",
                        "propertiesCommand"});
        return group.createPopupMenu();
    }

    private class PropertiesExecutor extends AbstractActionCommandExecutor {
        public void execute() {
            if (getSelectedPiece() != null) {
                piecePropertiesExecutor.execute();
            }
        }
    }

    private class DeleteExecutor extends AbstractActionCommandExecutor {
        public void execute() {
            int pieceCount = 0;
            int imageCount = 0;
            int imageVersionCount = 0;
            final java.util.List<DefaultMutableTreeNode> nodesToDelete = new ArrayList<DefaultMutableTreeNode>();
            TreePath[] paths = pieceTree.getSelectionPaths();
            for (TreePath path : paths) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
                if (node.isRoot()) {
                    continue;
                }
                if (node.getUserObject() instanceof Piece) {
                    pieceCount++;
                }
                nodesToDelete.add(node);
            }

            ConfirmationDialog dialog = new ConfirmationDialog() {
                protected void onConfirm() {
                    for (DefaultMutableTreeNode node : nodesToDelete) {
                        pieceTreeModel.removeNodeFromParent(node);
                        if (node.getUserObject() instanceof Piece) {
                            pieceService.remove((Piece) node.getUserObject());
                        }
                    }
                }
            };
            if (pieceCount > 0) {
                dialog.setTitle(getMessage("confirmDeletePieceDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeletePieceDialog.label"));
            } else if (imageCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteImageDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteImageDialog.label"));
            } else if (imageVersionCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteImageVersionDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteImageVersionDialog.label"));
            }

            dialog.showDialog();
        }
    }

    // TODO: Move all usefull classes out of this mess!
    private class PiecePropertiesExecutor extends AbstractActionCommandExecutor {

        private HierarchicalFormModel pieceFormModel;

        private PieceForm pieceForm;

        private CompositeDialogPage compositePage;

        public void execute() {
            final Piece piece = getSelectedPiece();
            pieceFormModel = FormModelHelper.createCompoundFormModel(piece);
            pieceForm = new PieceForm(artistService, FormModelHelper.createChildPageFormModel(pieceFormModel, null));

            compositePage = new TabbedDialogPage("pieceProperties");
            compositePage.addForm(pieceForm);

            TitledPageApplicationDialog dialog = new TitledPageApplicationDialog(compositePage, getWindowControl()) {
                protected void onAboutToShow() {
                    pieceForm.requestFocusInWindow();
                    setEnabled(compositePage.isPageComplete());
                }

                protected boolean onFinish() {
                    pieceFormModel.commit();
                    // also update the version
                    pieceService.saveOrUpdate(piece);
                    pieceTreeModel.nodeChanged(getSelectedPieceNode());
                    return true;
                }
            };
            dialog.showDialog();
        }
    }

}